package com.ruoyi.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.system.domain.BsJingqu;
import com.ruoyi.system.domain.BsJingquJingdian;
import com.ruoyi.system.domain.dto.BsJingquJingdianDto;
import com.ruoyi.system.domain.vo.BsJingquJingdianVo;
import com.ruoyi.system.mapper.BsJingquJingdianMapper;
import com.ruoyi.system.service.BsJingquJingdianService;
import com.ruoyi.system.service.BsJingquService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class BsJingquJingdianServiceImpl extends ServiceImpl<BsJingquJingdianMapper, BsJingquJingdian> implements BsJingquJingdianService{

    private final BsJingquService jingquService;

    private final DataSourceTransactionManager transactionManager;

    private final TransactionDefinition transactionDefinition;

    private final AsyncAccessJingdianFileService asyncAccessJingdianFileService;

    @Override
    public TableDataInfo<BsJingquJingdianDto> pageList(PageDomain pageDomain) {
        LambdaQueryWrapper<BsJingqu> jingquWrapper = new LambdaQueryWrapper<>();
        jingquWrapper.select(BsJingqu::getJingquId, BsJingqu::getJingquName, BsJingqu::getJingquAddress);
        jingquWrapper.orderByDesc(BsJingqu::getCreateTime);
        PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize());
        List<BsJingqu> jingquList = jingquService.list(jingquWrapper);
        PageInfo<BsJingqu> pageInfo = new PageInfo<>(jingquList);

        List<BsJingquJingdianDto> resultList = null;
        if (CollUtil.isNotEmpty(jingquList)) {
            List<Long> jingquIds = jingquList.stream().map(BsJingqu::getJingquId).collect(Collectors.toList());
            List<BsJingquJingdian> jingdianList = list(new LambdaQueryWrapper<BsJingquJingdian>().in(BsJingquJingdian::getJingquId, jingquIds));
            Map<Long, List<BsJingquJingdian>> jingdianGroup = jingdianList.stream().collect(Collectors.groupingBy(BsJingquJingdian::getJingquId));
            resultList = jingquList.parallelStream().map(i -> {
                List<BsJingquJingdian> tempJingdianList = jingdianGroup.get(i.getJingquId());
                i.setJingdianList(tempJingdianList);
                BsJingquJingdianDto result = BeanUtil.copyProperties(i, BsJingquJingdianDto.class);
                if (CollUtil.isNotEmpty(tempJingdianList)) {
                    long totalDuration = tempJingdianList.stream().mapToLong(BsJingquJingdian::getDuration).sum();
                    result.setTotalDuration(totalDuration);
                    result.setTotalDurationInfo(DateUtils.getTimePoor(totalDuration));
                    result.setTotalFile(tempJingdianList.size());
                } else {
                    result.setTotalDuration(0L);
                    result.setTotalDurationInfo("00:00:00");
                    result.setTotalFile(0);
                }
                return result;
            }).collect(Collectors.toList());
        }
        return new TableDataInfo<>(resultList, pageInfo.getTotal());
    }

    @Override
    public boolean saveEntity(BsJingquJingdianVo jingquJingdianVo) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        CountDownLatch latchMain = new CountDownLatch(1);
        CountDownLatch latchSlave = new CountDownLatch(jingquJingdianVo.getJingdianFiles().size());
        boolean flag = false;
        try {
            String nickName = SecurityUtils.getLoginUser().getUser().getNickName();
            List<BsJingquJingdian> saveList = new ArrayList<>();
            for (int i = 0; i < jingquJingdianVo.getJingdianFiles().size(); i++) {
                String jingdianFile = jingquJingdianVo.getJingdianFiles().get(i);
                String fileSuffix = jingdianFile.substring(jingdianFile.lastIndexOf(".") + 1);
                boolean isMedia = Arrays.stream(MimeTypeUtils.MEDIA_EXTENSION).anyMatch(x -> x.equalsIgnoreCase(fileSuffix));
                boolean isVideo = Arrays.stream(MimeTypeUtils.VIDEO_EXTENSION).anyMatch(x -> x.equalsIgnoreCase(fileSuffix));
                if (!isMedia && !isVideo) {
                    throw new BaseException("不支持的文件格式");
                }
                BsJingquJingdian jingdian = BeanUtil.copyProperties(jingquJingdianVo, BsJingquJingdian.class);
                jingdian.setJingdianName(jingquJingdianVo.getJingdianNames().get(i));
                jingdian.setJingdianFile(jingdianFile);
                // 异步线程security获取不到上下文信息 因为默认用的是ThreadLocal
                jingdian.setCreateBy(nickName);
                jingdian.setUpdateBy(nickName);
                saveList.add(jingdian);
            }

            for (BsJingquJingdian jingdian : saveList) {
                asyncAccessJingdianFileService.save(jingdian, atomicInteger, latchMain, latchSlave);
            }

            latchSlave.await();
            latchMain.countDown();
        } catch (Exception e) {
            e.printStackTrace();
            atomicInteger.getAndIncrement();
            latchMain.countDown();
        } finally {
            if (atomicInteger.get() == 0) {
                flag = true;
            }
        }
        return flag;
    }

    @Override
    public boolean updateEntity(BsJingquJingdianVo jingquJingdianVo) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        CountDownLatch latchMain = new CountDownLatch(1);
        CountDownLatch latchSlave = new CountDownLatch(jingquJingdianVo.getJingdianFiles().size());
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        boolean flag = false;
        try {
            String nickName = SecurityUtils.getLoginUser().getUser().getNickName();
            // 先删除之前的历史数据
            // delete后面跟的条件字段不是一个主键或者普通索引 会导致锁表
            LambdaQueryWrapper<BsJingquJingdian> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(BsJingquJingdian::getJingquId, jingquJingdianVo.getJingquId());
            List<BsJingquJingdian> jingdianList = list(queryWrapper);
            if (CollUtil.isNotEmpty(jingdianList)) {
                List<Long> jingdianIds = jingdianList.stream().map(BsJingquJingdian::getJingdianId).collect(Collectors.toList());
                removeByIds(jingdianIds);
            }

            List<BsJingquJingdian> updateList = new ArrayList<>();
            for (int i = 0; i < jingquJingdianVo.getJingdianFiles().size(); i++) {
                String jingdianFile = jingquJingdianVo.getJingdianFiles().get(i);
                String fileSuffix = jingdianFile.substring(jingdianFile.lastIndexOf(".") + 1);
                boolean isMedia = Arrays.stream(MimeTypeUtils.MEDIA_EXTENSION).anyMatch(x -> x.equalsIgnoreCase(fileSuffix));
                boolean isVideo = Arrays.stream(MimeTypeUtils.VIDEO_EXTENSION).anyMatch(x -> x.equalsIgnoreCase(fileSuffix));
                if (!isMedia && !isVideo) {
                    throw new BaseException("不支持的文件格式");
                }
                BsJingquJingdian jingdian = BeanUtil.copyProperties(jingquJingdianVo, BsJingquJingdian.class);
                jingdian.setJingdianName(jingquJingdianVo.getJingdianNames().get(i));
                jingdian.setJingdianFile(jingdianFile);
                // 异步线程security获取不到上下文信息 因为默认用的是ThreadLocal
                jingdian.setUpdateBy(nickName);
                updateList.add(jingdian);
            }

            for (BsJingquJingdian jingdian : updateList) {
                asyncAccessJingdianFileService.save(jingdian, atomicInteger, latchMain, latchSlave);
            }

            latchSlave.await();
            latchMain.countDown();
        } catch (Exception e) {
            e.printStackTrace();
            atomicInteger.getAndIncrement();
            latchMain.countDown();
        } finally {
            if (atomicInteger.get() > 0) {
                log.error("程序存在异常，程序进行回滚, 当前线程：{}", Thread.currentThread().getName());
                transactionManager.rollback(transaction);
            } else {
                //手动提交
                transactionManager.commit(transaction);
                flag = true;
            }
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        Assert.notNull(id, "主键id不能为空");
        LambdaQueryWrapper<BsJingquJingdian> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(BsJingquJingdian::getJingquId, id);
        return remove(deleteWrapper);
    }

}




